home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / public / bit / src / quant.c < prev    next >
C/C++ Source or Header  |  1994-08-01  |  21KB  |  813 lines

  1. /*
  2.  * $Id: quant.c,v 0.91 1994/02/20 00:53:04 zhao Pre-Release $
  3.  *
  4.  *. This file is part of BIT shareware package. After the two weeks of
  5.  *  free evaluation period, you are encouraged (required) to register
  6.  *  your copy for a small registration fee, which is $35 for personal use
  7.  *  and $50 for commercial, government and institutional use.
  8.  *
  9.  *  Copyright(c) 1993, 1994 by T.C. Zhao.
  10.  *  All rights reserved.
  11.  *
  12.  *  Permission to use, copy, and distribute this software in its entirety
  13.  *  for non-commercial purposes is hereby granted, provided that the
  14.  *  above shareware and copyright notices and this permission notice
  15.  *  appear in all copies and their documentation.
  16.  *
  17.  *  This software may be modified for your own use, but modified versions
  18.  *  may not be distributed without prior consent of the author.
  19.  *
  20.  *  This software is provided "as is" without expressed or implied
  21.  *  warranty of any kind.
  22.  *
  23.  *.
  24.  *
  25.  * Quantization:
  26.  *  Q256: Based on ppmqvga.c by Bill Davidsen(davdsen@crd.ge.com)
  27.  *  which was in turn based on ppmq256 and ppmq256fs by Lyle Rains
  28.  *  mediancut: not implemented
  29.  *  octree:    not implemented
  30.  */
  31. #if !defined(lint) && defined(F_ID)
  32. char *id_quant = " $Id: quant.c,v 0.91 1994/02/20 00:53:04 zhao Pre-Release $";
  33. #endif
  34.  
  35. #include "bit.h"
  36. #include "dmalloc.h"
  37. #include <math.h>
  38.  
  39. /* #define Q_DEBUG */
  40.  
  41. /***** Some definations, also used by init_configsys() ******/
  42. const char *qstring[] =
  43. {
  44.     "Q256", "MedianCut", "Octree", 0
  45. };
  46.  
  47. /***** init_configsys will call this routine *****/
  48.  
  49. const char *
  50. quant_opt_string(void)
  51. {
  52.     return "Ask|Q256|MedianCut|Octree";
  53. }
  54.  
  55. extern int qfast256(IPTR im);
  56. extern int qmediancut(IPTR im);
  57. extern int qoctree(IPTR im);
  58.  
  59. /***** dithering *****/
  60.  
  61. static const char *qdstring[] =
  62. {
  63.     "Floyd-Steinberg", "None", 0
  64. };
  65.  
  66. enum
  67.   {
  68.       QD_fs, QD_NONE
  69.   };
  70.  
  71. const char *
  72. qdither_opt_string(void)
  73. {
  74.     return "Floyd-Steinberg|None";
  75. }
  76.  
  77. /******* common limits to all quantizers **********/
  78.  
  79. #define MAXQBITS  CMAPBITS    /* defined in utype.h */
  80.  
  81. #ifndef RBITS
  82. #define RBITS  5
  83. #endif
  84.  
  85. #ifndef GBITS
  86. #define GBITS  6
  87. #endif
  88.  
  89. #ifndef BBITS
  90. #define BBITS  5
  91. #endif
  92.  
  93. #define MAXQR  (1 << RBITS)
  94. #define MAXQG  (1 << GBITS)
  95. #define MAXQB  (1 << BBITS)
  96. #define normr (PCBITS - RBITS)
  97. #define normg (PCBITS - GBITS)
  98. #define normb (PCBITS - BBITS)
  99.  
  100. typedef int cube_t;        /* histogram type     */
  101. static cube_t ***cube;        /* the actual one     */
  102. static int qcolors = 256;    /* final colors       */
  103. static int qdither, qmethod;    /* method and dither  */
  104.  
  105. void
  106. set_quant_parameters(int qc, int qd, int qm)
  107. {
  108.     if (qc > 0)
  109.     qcolors = qc;
  110.     if (qd >= 0)
  111.     qdither = qd;
  112.     if (qm >= 0)
  113.     qmethod = qm;
  114. }
  115.  
  116. /*********************************************************************
  117.  * used by type conversion routine. Basically get proper prototype
  118.  *********************************************************************/
  119.  
  120. extern int quant_method;
  121.  
  122. int
  123. pre_quant(void)
  124. {
  125.     return (quant_method <= 0) ?
  126.     get_quant_p("Quantization", &qmethod, &qdither, &qcolors, 0, 0, 0, 0) :
  127.     0;
  128. }
  129.  
  130. /* get histogram, taking care of overflow. cb must be initialized */
  131. static int
  132. get_true_hist(IPTR im, cube_t *** cb)
  133. {
  134.     register rgba_t *rgba = im->raster, *rs;
  135.     register unsigned r, g, b;
  136.     register int uc = 0;
  137.  
  138.     show_busy("Histogram ...");
  139.     for (rs = rgba + im->w * im->h; rgba < rs; rgba++)
  140.       {
  141.       Unpack(*rgba, r, g, b);
  142.       r >>= normr;
  143.       g >>= normg;
  144.       b >>= normb;
  145.       if (!cb[r][g][b])
  146.           uc++;
  147.       if ((++(cb[r][g][b])) <= 0)    /* check for overflow */
  148.           (cb[r][g][b])--;
  149.       }
  150.     end_busy();
  151.     return uc;
  152. }
  153.  
  154. /***********************************************************
  155.  * Global routine that does the quantization
  156.  ************************************************************/
  157.  
  158. int
  159. rgb_to_cmap(IPTR im)
  160. {
  161.     int status;
  162.     /* currently only fast256 is impletmentd */
  163.  
  164.     deactivate_all_forms();
  165.     switch (qmethod)
  166.       {
  167.       default:
  168.       status = qfast256(im);
  169.       break;
  170.       }
  171.     fl_activate_all_forms();
  172.     return status;
  173. }
  174.  
  175. /******************************************************************
  176.  * FAST 256
  177.  *  Based on ppmqvga.c by Bill Davidsen(davdsen@crd.ge.com)
  178.  *  which was in turn based on ppmq256 and ppmq256fs by Lyle Rains
  179.  ******************************************************************{*/
  180.  
  181. #define MAXWEIGHT 128
  182. #define STDWEIGHT_DIV  (2 << 8)
  183. #define STDWEIGHT_MUL  (2 << 10)
  184. #define GAIN       4
  185.  
  186. static int clutx;
  187. static int weight_convert[MAXWEIGHT];
  188. static int total_weight, cum_weight[MAXQG];
  189. static int rep_weight, rep_threshold;
  190. static int dr, dg, db;
  191. static CMPTR cm;
  192.  
  193. static void
  194. diffuse(register int r, register int g, register int b)
  195. {
  196.     register int _7_32nds, _3_32nds, _1_16th;
  197.  
  198.  
  199.     if (clutx < qcolors)
  200.       {
  201.       if (cube[r][g][b] > rep_threshold)
  202.         {
  203.         cm->ct[0][clutx] = (((2 * r + 1) * PCMAX) / (2 * MAXQR));
  204.         cm->ct[1][clutx] = (((2 * g + 1) * PCMAX) / (2 * MAXQG));
  205.         cm->ct[2][clutx] = (((2 * b + 1) * PCMAX) / (2 * MAXQB));
  206. #ifdef Q_DEBUG
  207.         if ((clutx & 3) == 0)
  208.           {
  209.               fprintf(stderr, "\n  %3d (%2d): ", clutx, rep_threshold);
  210.           }
  211.         fprintf(stderr, " (%03d,%03d,%03d)", cm->ct[0][clutx],
  212.             cm->ct[1][clutx], cm->ct[2][clutx]);
  213. #endif
  214.         ++clutx;
  215.         cube[r][g][b] -= rep_weight;
  216.         }
  217.       _7_32nds = (7 * cube[r][g][b]) / 32;
  218.       _3_32nds = (3 * cube[r][g][b]) / 32;
  219.       _1_16th = cube[r][g][b] - 3 * (_7_32nds + _3_32nds);
  220.       cube[r][g][b] = 0;
  221.       /* spread error evenly in color space. */
  222.       cube[r][g][b + db] += _7_32nds;
  223.       cube[r][g + dg][b] += _7_32nds;
  224.       cube[r + dr][g][b] += _7_32nds;
  225.       cube[r][g + dg][b + db] += _3_32nds;
  226.       cube[r + dr][g][b + db] += _3_32nds;
  227.       cube[r + dr][g + dg][b] += _3_32nds;
  228.       cube[r + dr][g + dg][b + db] += _1_16th;
  229.       /*
  230.        * Conserve the error at edges if possible (which it is, except the
  231.        * last pixel)
  232.        */
  233.       if (cube[r][g][b] != 0)
  234.         {
  235.         if (dg != 0)
  236.             cube[r][g + dg][b] += cube[r][g][b];
  237.         else if (dr != 0)
  238.             cube[r + dr][g][b] += cube[r][g][b];
  239.         else if (db != 0)
  240.             cube[r][g][b + db] += cube[r][g][b];
  241.         else
  242.           {
  243.               M_info("Quant", "lost error term");
  244.           }
  245.         }
  246.       }
  247.     cube[r][g][b] = -1;
  248. }
  249.  
  250. static int
  251. nearest_color(register rgba_t rgba)
  252. {
  253.     static unsigned long lut[3][2 * PCMAX + 1];
  254.     register unsigned long *tlut1 = lut[0] + PCMAX;
  255.     register unsigned long *tlut2 = lut[1] + PCMAX;
  256.     register unsigned long *tlut3 = lut[2] + PCMAX;
  257.     register unsigned i;
  258.     register unsigned long min_dist_sqd, dist_sqd;
  259.     register unsigned r, g, b;
  260.     register cube_t *cache;
  261.     register int nearest = 0;
  262.     static int lutinit;
  263.  
  264.     if (!lutinit)
  265.       {
  266.       for (i = 0; i < PCMAX; i++)
  267.         {
  268.         tlut1[i] = tlut1[-i] = 3 * (i * i);
  269.         tlut2[i] = tlut2[-i] = 4 * (i * i);
  270.         tlut3[i] = tlut3[-i] = 2 * (i * i);
  271.         }
  272.       lutinit = 1;
  273.       }
  274.     Unpack(rgba, r, g, b);
  275.     cache = &(cube[r >> normr][g >> normg][b >> normb]);
  276.  
  277.     if (*cache >= 0 && *cache < qcolors)
  278.     return *cache;
  279.  
  280.     min_dist_sqd = ~0;
  281.     for (i = 0; i < qcolors; ++i)
  282.       {
  283.       dist_sqd = *(tlut1 + r - cm->ct[0][i]) +
  284.           *(tlut2 + g - cm->ct[1][i]) +
  285.           *(tlut3 + b - cm->ct[2][i]);
  286.       if (dist_sqd < min_dist_sqd)
  287.         {
  288.         nearest = i;
  289.         min_dist_sqd = dist_sqd;
  290.         }
  291.       }
  292.     return (*cache = nearest);
  293. }
  294.  
  295. /* Errors are carried at FS_SCALE times actual size for accuracy */
  296. #define _7x16ths(x)   ((7 * (x)) / 16)
  297. #define _5x16ths(x)   ((5 * (x)) / 16)
  298. #define _3x16ths(x)   ((3 * (x)) / 16)
  299. #define _1x16th(x)    ((x) / 16)
  300. #define NEXT(line)    (!(line))
  301. #define FS_SCALE      1024
  302. typedef int fs_err_array[2][3];
  303.  
  304. #if 0                /* disable function version of diffuse code */
  305.  
  306. static void
  307. fs_diffuse(fs_err_array * fs_err, int line, int c, int err)
  308. {
  309.     fs_err[1][line][c] += _7x16ths(err);
  310.     fs_err[-1][NEXT(line)][c] += _3x16ths(err);
  311.     fs_err[0][NEXT(line)][c] += _5x16ths(err);
  312.     fs_err[1][NEXT(line)][c] = _1x16th(err);
  313. }
  314.  
  315. #else
  316.  
  317. #define fs_diffuse(fs_err, line, c, err)            \
  318.      do {                                           \
  319.        fs_err[1][line][c] += _7x16ths(err);         \
  320.        fs_err[-1][NEXT(line)][c] += _3x16ths(err);  \
  321.        fs_err[0][NEXT(line)][c] += _5x16ths(err);   \
  322.        fs_err[1][NEXT(line)][c] = _1x16th(err);     \
  323.      } while (ZERO)
  324.  
  325. #endif
  326.  
  327. typedef int erropt_t[4];
  328.  
  329. /* the main routine for fast256 */
  330. int
  331. qfast256(IPTR im)
  332. {
  333.     register rgba_t *rgba = im->raster, *rs;
  334.     register int r, g, b;
  335.     int i, j, k, fs_line = 0, row, ok;
  336.     int nearest, *errP;
  337.     fs_err_array *fs_err_lines = 0, *fs_err = 0;
  338.     register ci_t *ci, **cci;
  339.     register int scmax = FS_SCALE * PCMAXV;
  340.     static erropt_t *erropt;
  341.     long quant_rlines;
  342.  
  343.     check_emergency();
  344.  
  345.     clutx = 0;
  346.     /* get all the memory needed before we do anything */
  347.     set_use_calloc(1);
  348.     ok = ((erropt = calloc(qcolors, sizeof(erropt_t))) != 0 &&
  349.       (cube = get_3d(MAXQR, MAXQG, MAXQB, sizeof(cube_t))) != 0);
  350.     set_use_calloc(0);
  351.  
  352.     if (!ok)
  353.       {
  354.       Free(erropt);
  355.       free_3d(cube);
  356.       Bark("Quant", "malloc failed");
  357.       return -1;
  358.       }
  359.  
  360.     if (qdither == QD_fs)
  361.       {
  362.       if (!(fs_err_lines = calloc(im->w + 2, sizeof(fs_err_array))))
  363.         {
  364.         Bark("Quant", "malloc failed");
  365.         Free(erropt);
  366.         free_3d(cube);
  367.         return -1;
  368.         }
  369.       }
  370.  
  371.     /* get no. of colors with clustering */
  372.     cm = im->cmap;
  373.     check_emergency();
  374.  
  375.     (void) get_true_hist(im, cube);
  376.  
  377.     /* get weight */
  378.  
  379.     /* Initialize logarithmic weighing table */
  380.     weight_convert[0] = weight_convert[1] = 0;
  381.     for (i = 2; i < MAXWEIGHT; ++i)
  382.       {
  383.       weight_convert[i] = (int) (100.0 * log((double) (i)));
  384.       }
  385.  
  386.     k = im->w * im->h;
  387.     if ((k /= STDWEIGHT_DIV) == 0)
  388.     k = 1;
  389.  
  390.     total_weight = i = 0;
  391.     quant_rlines = progress_report("Generating weight ...", MAXQG);
  392.     for (g = 0; g < MAXQG; g++)
  393.       {
  394.       REPORT(g, quant_rlines);
  395.       for (r = 0; r < MAXQR; r++)
  396.         {
  397.         register unsigned long weight;
  398.         for (b = 0; b < MAXQB; b++)
  399.           {
  400.               /* Normalize the weights, independent of picture size. */
  401.               weight = cube[r][g][b] * STDWEIGHT_MUL;
  402.               if ((weight /= k))
  403.               i++;
  404.               if (weight >= MAXWEIGHT)
  405.               weight = MAXWEIGHT - 1;
  406.               total_weight += (cube[r][g][b] = weight_convert[weight]);
  407.           }
  408.         }
  409.       cum_weight[g] = total_weight;
  410.       }
  411.     rep_weight = total_weight / qcolors;
  412.     sprintf(im->misc, "c=%d d=%s u=%d", qcolors,
  413.         (qdither == QD_fs) ? "y" : "n", i);
  414.     update_image_info(im);
  415.  
  416.     im->colors = i;
  417.  
  418.     /* select threshold */
  419.     rep_threshold = total_weight * (28 + 110000 / i) / 95000;
  420.  
  421. #ifdef Q_DEBUG
  422.     fprintf(stderr, "found %d colors with total weight %d\n", i, total_weight);
  423.     fprintf(stderr, "avg weight for colors used  = %7.2f\n",
  424.         (float) total_weight / i);
  425.     fprintf(stderr, "avg weight for all colors   = %7.2f\n",
  426.         (float) total_weight / (MAXQR * MAXQG * MAXQB));
  427.     fprintf(stderr, "avg weight for final colors = %4d\n", rep_weight);
  428.     fprintf(stderr, "final rep_threshold = %d\n", rep_threshold);
  429. #endif
  430.  
  431.     quant_rlines = progress_report("Getting threshold ...", MAXQG);
  432.     dg = 1;
  433.     for (g = 0; g < MAXQG; ++g)
  434.       {
  435.       dr = 1;
  436.       for (r = 0; r < MAXQR; ++r)
  437.         {
  438.         db = 1;
  439.         for (b = 0; b < MAXQB - 1; ++b)
  440.             diffuse(r, g, b);
  441.         db = 0;
  442.         diffuse(r, g, b);
  443.         ++b;
  444.         if (++r == MAXQR - 1)
  445.             dr = 0;
  446.         db = -1;
  447.         while (--b > 0)
  448.             diffuse(r, g, b);
  449.         db = 0;
  450.         diffuse(r, g, b);
  451.         }
  452.  
  453.       if ((j = clutx - (qcolors * cum_weight[g]) / total_weight) != 0)
  454.         {
  455.         rep_threshold += j * GAIN;
  456.         }
  457.       if (++g == MAXQG - 1)
  458.           dg = 0;
  459.       dr = -1;
  460.       while (r-- > 0)
  461.         {
  462.         db = 1;
  463.         for (b = 0; b < MAXQB - 1; ++b)
  464.             diffuse(r, g, b);
  465.         db = 0;
  466.         diffuse(r, g, b);
  467.         ++b;
  468.         if (--r == 0)
  469.             dr = 0;
  470.         db = -1;
  471.         while (--b > 0)
  472.             diffuse(r, g, b);
  473.         db = 0;
  474.         diffuse(r, g, b);
  475.         }
  476.       /* Modify threshold to keep rep points proportionally distribited */
  477.       if ((j = clutx - (qcolors * cum_weight[g]) / total_weight) != 0)
  478.         {
  479.         rep_threshold += j * GAIN;
  480.         }
  481.       if ((++ok % 8) == 0)
  482.           update_progress_report(g);
  483.       M_info("quant", "diffuseG %d", g);
  484.       }
  485.  
  486.     M_info("quant", "DiffuseDone");
  487.  
  488.     /* check errors and reduce colormap */
  489.     quant_rlines = progress_report("Reducing colors ...", im->h);
  490.     for (row = im->h - 1; row >= 0; row--)
  491.       {
  492.       REPORT(im->h - 1 - row, quant_rlines);
  493.       rgba = ((rgba_t **) im->mraster)[row];
  494.       for (rs = rgba + im->w; rgba < rs; rgba++)
  495.         {
  496.         nearest = nearest_color(*rgba);
  497.         CPACK2RGB(*rgba, r, g, b);
  498.         errP = erropt[nearest];
  499.         errP[0] += r - cm->ct[0][nearest];
  500.         errP[1] += g - cm->ct[1][nearest];
  501.         errP[2] += b - cm->ct[2][nearest];
  502.         ++errP[3];
  503.         }
  504.       }
  505.  
  506. #ifdef Q_DEBUG
  507.     fprintf(stderr, "Color    Red Err  Green Err   Blue Err  Count\n");
  508. #endif
  509.  
  510.     for (i = 0; i < qcolors; i++)
  511.       {
  512.       errP = erropt[i];
  513.       j = errP[3];
  514.       if (j > 0)
  515.         {
  516.         j *= 4;
  517. #ifdef Q_DEBUG
  518.         fprintf(stderr, "%4d %10d %10d %10d %6d",
  519.             i, errP[0] / j, errP[1] / j, errP[2] / j, j);
  520. #endif
  521.         cm->ct[0][i] += (errP[0] / j) * 4;
  522.         cm->ct[1][i] += (errP[1] / j) * 4;
  523.         cm->ct[2][i] += (errP[2] / j) * 4;
  524.         }
  525.       }
  526.  
  527.     /* reset cache */
  528.  
  529.     for (r = 0; r < MAXQR; r++)
  530.     for (g = 0; g < MAXQG; g++)
  531.         for (b = 0; b < MAXQB; b++)
  532.         cube[r][g][b] = -1;
  533.  
  534.     /************* map colors **********************/
  535.  
  536.     cm->colors = clutx;
  537.     if (clutx < im->colors)
  538.     im->colors = clutx;
  539.  
  540.  
  541.     quant_rlines = progress_report("Maping colors ...", im->h);
  542.  
  543.     if (!(cci = get_mat(im->h, im->w, sizeof(ci_t))))
  544.       return -1;
  545.  
  546.     for (row = 0; row < im->h; row++)
  547.       {
  548.       REPORT(row, quant_rlines);
  549.  
  550.       if (qdither == QD_fs)
  551.         {
  552.         fs_err = fs_err_lines + 1;
  553.         fs_err[0][NEXT(fs_line)][0] = 0;
  554.         fs_err[0][NEXT(fs_line)][1] = 0;
  555.         fs_err[0][NEXT(fs_line)][2] = 0;
  556.         }
  557.  
  558.       rgba = ((rgba_t **) im->mraster)[row];
  559.       ci = cci[row];
  560.       for (rs = rgba + im->w; rgba < rs; rgba++, ci++)
  561.         {
  562.         if (qdither == QD_fs)
  563.           {
  564.               CPACK2RGB(*rgba, r, g, b);
  565.               r = FS_SCALE * r + fs_err[0][fs_line][0];
  566.               Range(r, 0, scmax);
  567.  
  568.               g = FS_SCALE * g + fs_err[0][fs_line][1];
  569.               Range(g, 0, scmax);
  570.  
  571.               b = FS_SCALE * b + fs_err[0][fs_line][2];
  572.               Range(b, 0, scmax);
  573.  
  574.               *rgba = RGB2CPACK((r / FS_SCALE),
  575.                     (g / FS_SCALE),
  576.                     (b / FS_SCALE));
  577.           }
  578.         nearest = nearest_color(*rgba);
  579.  
  580.         if (qdither == QD_fs)
  581.           {
  582.               r -= FS_SCALE * cm->ct[0][nearest];
  583.               g -= FS_SCALE * cm->ct[1][nearest];
  584.               b -= FS_SCALE * cm->ct[2][nearest];
  585.  
  586.               fs_diffuse(fs_err, fs_line, 0, r);
  587.               fs_diffuse(fs_err, fs_line, 1, g);
  588.               fs_diffuse(fs_err, fs_line, 2, b);
  589.           }
  590.         *ci = nearest;
  591.         if (qdither == QD_fs)
  592.             ++fs_err;
  593.         }
  594.       fs_line = NEXT(fs_line);
  595.       }
  596.  
  597.     M_info("quant", "Done");
  598. #ifdef Q_DEBUG
  599.     fprintf(stderr, "Final Colors\n");
  600.     for (i = 0; i < qcolors; i++)
  601.     fprintf(stderr, "%4d: %3d %3d %3d\n", i, cm->ct[0][i],
  602.         cm->ct[1][i], cm->ct[2][i]);
  603. #endif
  604.     if (qdither == QD_fs)
  605.     free(fs_err_lines);
  606.  
  607.     Free(erropt);
  608.     free_3d(cube);
  609.  
  610.     (void) fill_image_struct(im, cci, im->h, im->w,
  611.                  IS_GRAY(im) ? T_GMAP : T_CMAP);
  612.  
  613.     M_info("QuantReplaceImage", "Done");
  614.     remove_progress_report();
  615.     return 0;
  616. }
  617.  
  618. /***********************************************************
  619.  * End of Qfast256
  620.  ************************************************************}*/
  621.  
  622. /***************************************************************
  623.  * GUI part of the quantization
  624.  ***************************************************************/
  625.  
  626. static FL_FORM *quantform;
  627. static FL_OBJECT *qtitle, *mchoice, *dichoice, *qreport, *qcancel, *qok;
  628. static FL_OBJECT *qmisc1, *qmisc2;
  629. static int qnc = 256, qncmax = MAXCML;
  630. static void create_form_quant(void);
  631. void
  632. set_quant_max_color(int c)
  633. {
  634.     qncmax = c;
  635. }
  636.  
  637. /* ARGSUSED */
  638. static void
  639. qnc_cb(FL_OBJECT * ob, long q)
  640. {
  641.     char pp[10];
  642.  
  643.     if (q == 2)
  644.     qnc <<= 1;
  645.     else if (q == -2)
  646.     qnc >>= 1;
  647.     else
  648.     qnc += q;
  649.  
  650.     if (qnc < 8)
  651.     qnc = 8;
  652.     else if (qnc > qncmax)
  653.     qnc = qncmax;
  654.     sprintf(pp, "%d", qnc);
  655.     fl_set_object_label(qreport, pp);
  656. }
  657. int
  658. get_quant_p(const char *title, int *qm, int *dm, int *qc,
  659.         const char *misc1, int *s1, const char *misc2, int *s2)
  660. {
  661.  
  662.     FL_OBJECT *ret;
  663.     short val;
  664.  
  665.     create_form_quant();
  666.     qnc = *qc;
  667.     qnc_cb(0, 0);
  668.     fl_set_object_label(qtitle, title);
  669.     /* minus one because qstring is one more than available */
  670.     if (*qm < 0 || *qm >= (sizeof(qstring) / sizeof(char *)) - 1)
  671.      *qm = 0;
  672.     if (*dm < 0 || *dm >= (sizeof(qdstring) / sizeof(char *)) - 1)
  673.      *dm = 0;
  674.  
  675.     fl_set_choice(mchoice, *qm + 1);
  676.     fl_set_choice(dichoice, *dm + 1);
  677.     if (misc1 && *misc1)
  678.       {
  679.       fl_set_object_label(qmisc1, misc1);
  680.       fl_set_button(qmisc1, *s1);
  681.       }
  682.     else
  683.       {
  684.       fl_hide_object(qmisc1);
  685.       }
  686.  
  687.     if (misc2 && *misc2)
  688.       {
  689.       fl_set_object_label(qmisc2, misc2);
  690.       fl_set_button(qmisc1, *s2);
  691.       }
  692.     else
  693.       {
  694.       fl_hide_object(qmisc2);
  695.       }
  696.  
  697.     bit_show_form(quantform, FL_PLACE_HOTSPOT, 0, title);
  698.     while ((ret = fl_do_forms()) != qcancel && ret != qok)
  699.       {
  700.       if (ret == FL_EVENT)
  701.           (void) bit_qread(&val);
  702.       }
  703.  
  704.     if (ret != qcancel)
  705.       {
  706.       *qc = qnc;
  707.       *qm = fl_get_choice(mchoice) - 1;
  708.       *dm = fl_get_choice(dichoice) - 1;
  709.       if (misc1 && *misc1)
  710.           *s1 = fl_get_button(qmisc1);
  711.       if (misc2 && *misc2)
  712.           *s2 = fl_get_button(qmisc2);
  713.       }
  714.     bit_hide_form(quantform);
  715.     qncmax = MAXCML;
  716.     return (ret == qcancel) ? -1 : 0;
  717. }
  718.  
  719. static void
  720. create_form_quant(void)
  721. {
  722.     FL_OBJECT *obj;
  723.     int i;
  724.     static int ok;
  725.  
  726.     if (ok)
  727.     return;
  728.  
  729.     quantform = fl_bgn_form(FL_NO_BOX, 225.0, 200.0);
  730.     obj = fl_add_box(FL_UP_BOX, 0.0, 0.0, 225.0, 200.0, "");
  731.     fl_set_object_lcol(obj, 4);
  732.     obj = fl_add_button(FL_HIDDEN_BUTTON, 0, 0, 225, 200.0, "");
  733.     fl_set_call_back(obj, help_cb, HELP_QUANT);
  734.     qtitle = obj = fl_add_text(FL_NT, 35.0, 165.0, 175.0, 25.0, "");
  735.     fl_set_object_color(obj, 4, 47);
  736.     fl_set_object_lcol(obj, 4);
  737.     fl_set_object_align(obj, FL_ALIGN_CENTER);
  738.     fl_set_object_lstyle(obj, FL_BOLD_STYLE);
  739.  
  740.     /* quant method */
  741.     mchoice = obj = fl_add_choice(FL_NC, 60.0, 130.0, 150.0, 25.0, "Method");
  742.     fl_set_object_boxtype(obj, FL_FRAME_BOX);
  743.     fl_set_object_lcol(obj, 4);
  744.     fl_set_object_lsize(obj, 10.0);
  745.     fl_set_choice_fontsize(obj, 10.0);
  746.  
  747.     i = 0;
  748.     while (qstring[i])
  749.       {
  750.       fl_addto_choice(obj, qstring[i]);
  751.       i++;
  752.       }
  753.  
  754.     dichoice = obj = fl_add_choice(FL_NC, 60.0, 100.0, 150.0, 25.0, "Dither");
  755.     fl_set_object_boxtype(obj, FL_FRAME_BOX);
  756.     fl_set_object_lcol(obj, 4);
  757.     fl_set_object_lsize(obj, 10.0);
  758.     fl_set_choice_fontsize(obj, 10.0);
  759.     i = 0;
  760.     while (qdstring[i])
  761.       {
  762.       fl_addto_choice(obj, qdstring[i]);
  763.       i++;
  764.       }
  765.  
  766.     /* no. of colors */
  767.     obj = fl_add_button(FL_TB, 185.0, 70.0, 25.0, 25.0, "@>>");
  768.     fl_set_object_boxtype(obj, FL_FRAME_BOX);
  769.     fl_set_object_color(obj, 47, 9);
  770.     fl_set_object_lcol(obj, 1);
  771.     fl_set_call_back(obj, qnc_cb, 2);
  772.     obj = fl_add_button(FL_TB, 160.0, 70.0, 25.0, 25.0, "@>");
  773.     fl_set_object_boxtype(obj, FL_FRAME_BOX);
  774.     fl_set_object_color(obj, 47, 9);
  775.     fl_set_object_lcol(obj, 1);
  776.     fl_set_call_back(obj, qnc_cb, 1);
  777.     obj = fl_add_button(FL_TB, 60.0, 70.0, 25.0, 25.0, "@<<");
  778.     fl_set_object_boxtype(obj, FL_FRAME_BOX);
  779.     fl_set_object_color(obj, 47, 9);
  780.     fl_set_object_lcol(obj, 1);
  781.     fl_set_call_back(obj, qnc_cb, -2);
  782.     obj = fl_add_button(FL_TB, 85.0, 70.0, 25.0, 25.0, "@<");
  783.     fl_set_object_boxtype(obj, FL_FRAME_BOX);
  784.     fl_set_object_color(obj, 47, 9);
  785.     fl_set_object_lcol(obj, 1);
  786.     fl_set_call_back(obj, qnc_cb, -1);
  787.     qreport = obj = fl_add_text(FL_NT, 110.0, 70.0, 50.0, 25.0, "");
  788.     fl_set_object_boxtype(obj, FL_FRAME_BOX);
  789.     fl_set_object_lcol(obj, 4);
  790.     fl_set_object_lsize(obj, 10.0);
  791.     fl_set_object_align(obj, FL_ALIGN_CENTER);
  792.     obj = fl_add_text(FL_NT, 5.0, 70.0, 50.0, 25.0, "Ncolors");
  793.     fl_set_object_lcol(obj, 4);
  794.     fl_set_object_lsize(obj, 10.0);
  795.  
  796.     /* misc buttons */
  797.     qmisc1 = obj = fl_add_roundbutton(FL_PB, 15.0, 35.0, 35.0, 30.0, "");
  798.     fl_set_object_lcol(obj, 4);
  799.     fl_set_object_lsize(obj, 10.0);
  800.     qmisc2 = obj = fl_add_roundbutton(FL_PB, 125.0, 35.0, 35.0, 30.0, "");
  801.     fl_set_object_lcol(obj, 4);
  802.     fl_set_object_lsize(obj, 10.0);
  803.     qcancel = obj = fl_add_button(FL_NB, 55.0, 10.0, 70.0, 25.0, "Cancel");
  804.     fl_set_object_color(obj, 47, 3);
  805.     fl_set_object_lsize(obj, 10.0);
  806.     qok = obj = fl_add_button(FL_NB, 125.0, 10.0, 70.0, 25.0, "OK");
  807.     fl_set_object_color(obj, 47, 2);
  808.     fl_set_object_lsize(obj, 10.0);
  809.     fl_end_form();
  810.     fl_set_form_hotspot(quantform, (125 + 35), 10 + 13);
  811.     ok = 1;
  812. }
  813.